// Creation History:
//---------------------------------------------
// COMPANY : HTHK
// BY      : Zhiguo Yang
// Date    : 30/03/2005
// CRLOG   : P999
//---------------------------------------------
//
/*
 * $Id: GeneralFactory.java,v 1.1.1.1 2013/08/16 05:35:48 tengyang Exp $
 *
 * $Log: GeneralFactory.java,v $
 * Revision 1.1.1.1  2013/08/16 05:35:48  tengyang
 * no message
 *
 * Revision 1.3  2005/03/31 02:53:43  zhyan
 * Change the file header
 *
 * Revision 1.2  2005/03/30 13:30:30  zhyan
 * Add the file header
 *
 * Revision 1.1  2005/03/21 13:31:58  zhyan
 * Add the generatl factory
 *
 */
package com.hp.gdcc.webframe.factory;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.ResourceBundle;

import org.apache.log4j.Logger;

/**
 * Interface manager is a catalog for keeping all interfaces It resolves
 * the dependency among subsystems using the interface
 * and implementation method. Each interface has a unique name and is
 * registered into the interface manager, the client of interface could
 * get the reference of the interface's implementation use #lookup method
 * of this class.
 *
 * <p>
 * During system initialization, #loadInterfaces is called. Interface
 * manager will load all implementation classes or objects defined in a
 * configuration file. The configuration file is simple text file, each
 * line contains one implementation class name. the line start with "#"
 * is regarded as a comment line, all empty lines are ignored. The
 * configuration file named as "ifs.def", and it must be in the same
 * package as the ICatalog class.
 * </p>
 *
 * <p>
 * All implementation class must has a static initialization section. In
 * this section, it calls one of the #registerInterface methods to
 * register itself to the interface manager. When interface manager
 * starts up, the implemtation class was defined in the configuration
 * file will be loaded, and the static initialization section will be
 * called.
 * </p>
 *
 * @author lizl Created at: 2003-5-18,20:05:16
 */
public final class GeneralFactory {
  private static Hashtable interfaces;
  private static Logger log = Logger.getLogger(GeneralFactory.class);

  static {
    interfaces = new Hashtable();
    load();
  }

  private GeneralFactory() {
  }

  public static synchronized void loadInterfaces() {
    load();
  }

  /**
   * load implementation information from a configuration file. the
   * configuration file must reside in the same package with the
   * interface manager class. and the configuration file is named as
   * "ifs.def"
   */
  private static void load() {
    // check the implemetation in resource bundle first
    try {
      ResourceBundle impls =
          ResourceBundle.getBundle(
          "com.hp.gdcc.commons.IDefaults");

      log.info("Try to load interface from ResourceBundle");

      if (impls != null) {
        log.info(
            "Load implementation classes from ResourceBundle class.");

        Enumeration e = impls.getKeys();

        while (e.hasMoreElements()) {
          String iname = (String) e.nextElement();
          String classname = impls.getString(iname);

          if (classname == null) {
            continue;
          }

          classname = classname.trim();

          if (classname.length() == 0) {
            continue;
          }

          try {
            Class clz = Class.forName(classname);
            Object instance = clz.newInstance();
            if (instance instanceof IManagable) {
              interfaces.put(iname, instance);
            } else {
              interfaces.put(iname, clz);
            }
            log.info("Load implementation class " + clz
                     + " for interface " + iname);
          }
          catch (Exception ex) {
            log.error("Class for interface " + iname
                      + " not found");
          }
        }
      }
    }
    catch (Exception e) {
      log.error(e, e);
      log.error("Unknown exception happened"
                + ", cannot load interface from IDefaults ResourceBundle."
                + ". Try to load from ifs.properties file");
    }

    // check the implemetation in resource file
    InputStream is =
        Thread.currentThread().getContextClassLoader()
        .getResourceAsStream("ifs.properties");

    log.info("Try to load interface from resource file");

    if (is != null) {
      log.info("Load implementation classes from Resource file.");

      LineNumberReader lnr =
          new LineNumberReader(new InputStreamReader(is));

      while (true) {
        String line;

        try {
          line = lnr.readLine();
        }
        catch (IOException e) {
          e.printStackTrace();

          continue;
        }

        if (line == null) {
          break;
        }

        line = line.trim();

        // empty line
        if (line.length() == 0) {
          continue;
        }

        // comments
        if (line.startsWith("#")) {
          continue;
        }

        String iname = line.substring(0, line.indexOf("="));
        String classname = line.substring(line.indexOf("=") + 1, line.length());

        try {
          Class clz = Class.forName(classname);
          Object instance = clz.newInstance();
          if (instance instanceof IManagable) {
            interfaces.put(iname, instance);
          } else {
            interfaces.put(iname, clz);
          }
          log.info("Loaded implementation class " + classname);
        }
        catch (Exception ex) {
          log.error("Class [" + classname + "] not found at line "
                    + lnr.getLineNumber());
        }
      }

      return;
    }
  }

  /**
   * get and implementation class' instance of the interface. User
   * should cast the returned Object to suitable interface.
   *
   * @param name the name of the interface
   *
   * @return the implementation object or null.
   */
  public static Object lookup(String name) {
    Object intf = interfaces.get(name);

    if (intf instanceof IManagable) {
      return intf;
    }

    if (intf instanceof Class) {
      Class clz = (Class) intf;
      Object obj = null;

      try {
        obj = clz.newInstance();
      }
      catch (InstantiationException e) {
        e.printStackTrace();
      }
      catch (IllegalAccessException e) {
        e.printStackTrace();
      }

      return obj;
    }

    return null;
  }

  /*
   * DOCUMENT ME!
   *
   * @param a DOCUMENT ME!
       public static void main(String[] a) {
      ICatalog.loadInterfaces();
       }*/

  /**
   * register an interface using the implementation class. When using
   * #lookup method get a reference of this implementation, ICatalog
   * will create a new instance of this class via calling newInstance()
   * method.
   *
   * @param name the name of the interface
   * @param clz the implementation class
   */
  public static void registerInterface(String name, Class clz) {
    interfaces.put(name, clz);
    log.info("Register Class " + clz.getName());
  }

  /**
   * register a interface using the pre-loaded implementation object.
   * That is to say, the implementation class is a sigleton. For every
   * #lookup, the interface manager return the same instance of the
   * implementation class' object.
   *
   * @param name the interface name
   * @param intf an object which class implemented IManagable interface
   */
  public static void registerInterface(String name, IManagable intf) {
    interfaces.put(name, intf);
    log.info("Register interface " + intf.getClass().getName());
  }
}
